package controllers

import (
	"fmt"
	"gogogo/helpers"
	"gogogo/models"
	cooo "gogogo/services/dtos"
	"math"
	"os"
	"time"

	"github.com/gofiber/fiber/v2"
	"github.com/golang-jwt/jwt/v4"
)

func JsonEchoHandler(c *fiber.Ctx) error {
	return c.Status(fiber.StatusOK).JSON(fiber.Map{
		"success": true,
		"message": "You are at the endpoint 😉",
		"cfg":     models.GetInstance().DbConfigs,
	})
}

func JsonPostEchoHandler(c *fiber.Ctx) error {
	var body cooo.Todo

	err := c.BodyParser(&body)

	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"success": false,
			"message": "Cannot parse JSON",
			"code":    helpers.ERROR_JSON_PARSE_ERROR,
		})
	} else {
		return c.Status(fiber.StatusOK).JSON(fiber.Map{
			"success": true,
			"data": fiber.Map{
				"todo": body,
			},
		})
	}
}

func DowakeUp(c *fiber.Ctx) error {
	return c.Status(fiber.StatusOK).JSON(fiber.Map{
		"success": true,
		"message": "You are at the endpoint 😉",
	})
}

func GetAppCfgs(c *fiber.Ctx) error {
	return c.Status(fiber.StatusOK).JSON(fiber.Map{
		"success": true,
		"message": "You are at the endpoint 😉",
	})
}

func UploadFile(c *fiber.Ctx) error {
	filename := c.Query("name")
	// file, err := c.FormFile("document")
	// if err == nil {
	// 	fmt.Println(file)
	// } else {
	// 	fmt.Println(err)
	// }
	// fmt.Println(c.Body())
	file, err := os.Create("./uploads/" + filename)
	if err != nil {
		file.Close()
		return c.Status(fiber.StatusOK).JSON(fiber.Map{
			"success": false,
		})
	}
	file.Write(c.Body())
	// // Check for errors:
	// if err == nil {
	// 	// 👷 Save file inside uploads folder under current working directory:
	// 	c.SaveFile(file, fmt.Sprintf("./uploads/%s", file.Filename))
	// }
	file.Close()
	return c.Status(fiber.StatusOK).JSON(fiber.Map{
		"success": true,
		"message": "upload OK 😉",
		"fileUrl": fmt.Sprintf("http://%s:3555/%s", helpers.GetCfgInstance().Conf.LocalURL, filename),
	})
}

func GetJwtForTest(c *fiber.Ctx) error {
	// Create token
	token := jwt.New(jwt.SigningMethodHS256)

	// Set claims
	claims := token.Claims.(jwt.MapClaims)
	claims["name"] = "John Doe"
	claims["admin"] = true
	claims["exp"] = time.Now().Add(time.Hour * 72).Unix()

	// Generate encoded token and send it as response.
	t, err := token.SignedString([]byte(helpers.GetCfgInstance().Conf.Jwt))
	if err != nil {
		return c.SendStatus(fiber.StatusInternalServerError)
	}
	return c.Status(fiber.StatusOK).JSON(fiber.Map{
		"success": true,
		"message": "You are at the endpoint 😉",
		"token":   t,
	})
}

func getJwt(uid int) (string, error) {
	token := jwt.New(jwt.SigningMethodHS256)

	// Set claims
	claims := token.Claims.(jwt.MapClaims)
	claims["u"] = uid
	claims["exp"] = time.Now().Add(time.Hour * 72).Unix()

	// Generate encoded token and send it as response.
	t, err := token.SignedString([]byte(helpers.GetCfgInstance().Conf.Jwt))
	return t, err
}

func getUserDetail(u *models.User, ub []*cooo.UserBuildingX, c *fiber.Ctx) error {
	token, err2 := getJwt(u.Id)
	if err2 != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"success": false,
			"message": "Cannot gen Token",
			"code":    helpers.ERROR_JSON_GEN_ERROR,
		})
	} else {
		uitem := models.GetInstance().GetUserItemList(u.Id)
		// 剩余看广告冷却时间
		adCD := 0
		lasttimeWatchAD := u.Advertisingtime
		nextWatchTime := lasttimeWatchAD.Add(time.Second * time.Duration(models.GetInstance().DbConfigs.Gp.Advertisingcd))
		now := time.Now()
		if now.Before(nextWatchTime) {
			adCD = int(nextWatchTime.Sub(now).Seconds())
		} else {
			adCD = 0
		}

		return c.Status(fiber.StatusOK).JSON(fiber.Map{
			"success":       true,
			"token":         token,
			"user":          u,
			"userbuilding":  ub,
			"items":         uitem,
			"advertisingCD": adCD,
		})
	}
}

func TurLogin(c *fiber.Ctx) error {
	var body cooo.TouristLogin

	err := c.BodyParser(&body)

	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"success": false,
			"message": "Cannot parse JSON",
			"code":    helpers.ERROR_JSON_PARSE_ERROR,
		})
	} else {
		u, ub := models.GetInstance().TouristLogin(body.MachineCode)
		if u == nil {
			return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
				"success": false,
				"message": "generate user failed",
				"code":    helpers.ERROR_USER_GEN_ERROR,
			})
		}
		return getUserDetail(u, ub, c)
	}
}

func Login(c *fiber.Ctx) error {
	var body cooo.UserLogin

	err := c.BodyParser(&body)

	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"success": false,
			"message": "Cannot parse JSON",
			"code":    helpers.ERROR_JSON_PARSE_ERROR,
		})
	} else {
		// todo platform check backend such as fb/wechat
		u, ub := models.GetInstance().UserLogin(body.Openid, body.Type)
		if u == nil {
			return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
				"success": false,
				"message": "generate user failed",
				"code":    helpers.ERROR_USER_GEN_ERROR,
			})
		}
		return getUserDetail(u, ub, c)
	}
}

func UserItems(c *fiber.Ctx) error {
	var body cooo.BaseRequestWithInt

	err := c.BodyParser(&body)

	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"success": false,
			"message": "Cannot parse JSON",
		})
	} else {
		u := models.GetInstance().GetUserItemList(body.Id)
		return c.Status(fiber.StatusOK).JSON(fiber.Map{
			"success": true,
			"items":   u,
		})
	}
}

func UserBuildings(c *fiber.Ctx) error {
	var body cooo.BaseRequestWithInt

	err := c.BodyParser(&body)

	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"success": false,
			"message": "Cannot parse JSON",
			"code":    helpers.ERROR_JSON_PARSE_ERROR,
		})
	} else {
		u := models.GetInstance().GetUserBuildings(body.Id)
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"success":   true,
			"buildings": u,
		})
	}
}

func DoBuildings(c *fiber.Ctx) error {
	var body cooo.UserBuild
	err := c.BodyParser(&body)

	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"success": false,
			"message": "Cannot parse JSON",
			"code":    helpers.ERROR_JSON_PARSE_ERROR,
		})
	} else {
		u := models.GetInstance().GetUser(body.Uid)
		if u.Id == 0 {
			return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
				"success": false,
				"message": "user not exist",
				"code":    helpers.ERROR_USER_NOT_EXIST,
			})
		} else {
			// 检索该建筑ID是否在用户的建筑队列里
			building := models.GetInstance().UserBuildingsIncludeNotFinished(body.Uid, body.Bid)
			if building.Id == 0 {
				return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
					"success": false,
					"message": "building not open or already finished",
					"code":    helpers.ERROR_BUILDING_NOT_OPEN,
				})
			} else {
				// 获取配置检查钱够不够
				step := models.GetInstance().GetSpecBuildingStep(building.Buildingid, building.Step)
				if step.Id == 0 {
					return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
						"success": false,
						"message": "building not open",
						"code":    helpers.ERROR_CONFIG_NOT_EXIST,
					})
				} else {
					if u.Coin < step.Coin {
						return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
							"success": false,
							"message": "coin not enough",
							"code":    helpers.ERROR_COIN_NOT_ENOUGH,
						})
					}

					// 扣钱，加声望，加经验，升级检测
					manorlv := models.GetInstance().GetSpecBuildingLevel(u.Manorlv)
					if manorlv.Id == 0 {
						return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
							"success": false,
							"message": "config not exist",
							"code":    helpers.ERROR_CONFIG_NOT_EXIST,
						})
					}
					leftExp := u.Manorexp + step.Exp
					usrlv := u.Manorlv
					if u.Manorexp+step.Exp > manorlv.Exp {
						// 升级了
						leftExp = u.Manorexp + step.Exp - manorlv.Exp
						usrlv += 1
					}
					u.Coin = u.Coin - step.Coin
					u.Manorlv = usrlv
					u.Manorexp = leftExp

					rowaffect := models.GetInstance().UpdateUser(u.Id, usrlv, leftExp, u.Coin)
					if rowaffect <= 0 {
						return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
							"success": false,
							"message": "update failed",
							"code":    helpers.ERROR_UPDATE_FAILED,
						})
					}

					// 检测是否完成该建筑，如果完成则开启新建筑
					building.Step += 1
					updateBuilding := make(map[string]interface{})
					var newBuilding []*cooo.UserBuildingX
					if building.Step >= building.Totalstep {
						// 更新旧建筑的步骤
						building.Step = building.Totalstep
						building.Skin = 1
						building.Showskin = 1
						updateBuilding["step"] = building.Step
						updateBuilding["skin"] = 1
						updateBuilding["showskin"] = 1
						models.GetInstance().UpdateBuilding(building.Id, updateBuilding)
						// 完成了建筑,查找后续建筑插入
						newBuilding = models.GetInstance().FinishUserBuilding(u.Id, step.Bid)
					} else {
						// 更新旧建筑的步骤
						updateBuilding["step"] = building.Step
						models.GetInstance().UpdateBuilding(building.Id, updateBuilding)
					}
					newBuildingReturn := newBuilding
					if newBuilding == nil {
						newBuildingReturn = make([]*cooo.UserBuildingX, 0)
					}
					u.Pwd = ""
					return c.Status(fiber.StatusOK).JSON(fiber.Map{
						"success":         true,
						"user":            u,
						"updatedBuilding": building,
						"newBuilding":     newBuildingReturn,
					})
				}
			}
		}
	}
}

func DoChangeSkins(c *fiber.Ctx) error {
	var body cooo.UserChangeSkin
	err := c.BodyParser(&body)

	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"success": false,
			"message": "Cannot parse JSON",
			"code":    helpers.ERROR_JSON_PARSE_ERROR,
		})
	}
	// 检索用户是否拥有此建筑以及此皮肤
	building := models.GetInstance().GetUserSpecBuilding(body.Bid, body.Uid)
	if building.Id == 0 {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"success": false,
			"message": "Building not existing or uncompleted",
			"code":    helpers.ERROR_BUILDING_NOT_EXIST,
		})
	}
	// 已经完成所有步骤的建筑才可以更换皮肤
	// 检测是否解锁该皮肤
	skin := building.Skin
	if skin&int(math.Pow(2, float64(body.Sid-1))) == 0 {
		// 皮肤未解锁
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"success": false,
			"message": "Skin not open",
			"code":    helpers.ERROR_SKIN_NOT_OPEN,
		})
	}
	// 皮肤已经解锁过,更换当前皮肤
	models.GetInstance().UpdateBuilding(building.Id, map[string]interface{}{"showskin": body.Sid})
	return c.Status(fiber.StatusOK).JSON(fiber.Map{
		"success":   true,
		"building":  body.Bid,
		"newSkinid": body.Sid,
	})
}

func UnlockSkins(c *fiber.Ctx) error {
	var body cooo.UserChangeSkin
	err := c.BodyParser(&body)

	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"success": false,
			"message": "Cannot parse JSON",
			"code":    helpers.ERROR_JSON_PARSE_ERROR,
		})
	}
	// 检索用户是否拥有此建筑以及此皮肤
	building := models.GetInstance().GetUserSpecBuilding(body.Bid, body.Uid)
	if building.Id == 0 {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"success": false,
			"message": "Building not existing or uncompleted",
			"code":    helpers.ERROR_BUILDING_NOT_EXIST,
		})
	}
	// 已经完成所有步骤的建筑才可以解锁皮肤
	// 检测是否已经解锁过该皮肤
	skin := building.Skin
	if skin&int(math.Pow(2, float64(body.Sid-1))) != 0 {
		// 皮肤已经解锁
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"success": false,
			"message": "Skin already open",
			"code":    helpers.ERROR_SKIN_ALREADY_OPEN,
		})
	}
	// 检测玩家是否有合成该皮肤的素材道具
	buildingSkin := models.GetInstance().GetBuildingSkinByBidAndSid(body.Bid, body.Sid)
	if buildingSkin.Id == 0 {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"success": false,
			"message": "Skin not exist",
			"code":    helpers.ERROR_DATA_NOT_EXIST,
		})
	}
	hitMap := make(map[int]bool)
	for i := 0; i < buildingSkin.Puzzle; i++ {
		hitMap[i+1] = false
	}
	items := models.GetInstance().GetUserItemsForSkin(body.Uid, body.Bid, body.Sid)
	for i := 0; i < len(items); i++ {
		hitMap[items[i].Seq] = true
	}

	finished := true
	for _, v := range hitMap {
		finished = finished && v
		if !finished {
			break
		}
	}

	if !finished {
		// 没有完成拼图,不能解锁
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"success": false,
			"message": "Skin not unlock",
			"code":    helpers.ERROR_SKIN_NOT_COMPLETE,
		})
	}
	// 扣减道具数量
	var updateIidList []int
	var deleteIidList []int
	var updateIdList []int
	var deleteIdList []int
	for i := 0; i < len(items); i++ {
		if items[i].Amount-1 <= 0 {
			deleteIdList = append(deleteIdList, items[i].Id)
			deleteIidList = append(deleteIdList, items[i].Iid)
		} else {
			updateIdList = append(updateIdList, items[i].Id)
			updateIidList = append(updateIdList, items[i].Iid)
		}
	}
	models.GetInstance().UserUnlockSkinUseItem(body.Uid, updateIdList, deleteIdList)
	// 解锁皮肤
	building.Skin = building.Skin | int(math.Pow(2, float64(body.Sid-1)))
	models.GetInstance().UpdateBuilding(building.Id, map[string]interface{}{"skin": building.Skin})
	// 加经验,或者升级
	u := models.GetInstance().GetUser(body.Uid)
	manorlv := models.GetInstance().GetSpecBuildingLevel(u.Manorlv)
	if manorlv.Id == 0 {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"success": false,
			"message": "config not exist",
			"code":    helpers.ERROR_CONFIG_NOT_EXIST,
		})
	}

	expadd := buildingSkin.Exp
	leftExp := u.Manorexp + expadd
	usrlv := u.Manorlv
	if u.Manorexp+expadd > manorlv.Exp {
		// 升级了
		leftExp = u.Manorexp + expadd - manorlv.Exp
		usrlv += 1
	}
	u.Manorlv = usrlv
	u.Manorexp = leftExp
	rowaffect := models.GetInstance().UpdateUser(u.Id, usrlv, leftExp, u.Coin)
	if rowaffect <= 0 {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"success": false,
			"message": "update failed",
			"code":    helpers.ERROR_UPDATE_FAILED,
		})
	}
	u.Pwd = ""
	if updateIidList == nil {
		updateIidList = make([]int, 0)
	}
	if deleteIidList == nil {
		deleteIidList = make([]int, 0)
	}
	// todo 加声望
	return c.Status(fiber.StatusOK).JSON(fiber.Map{
		"success":       true,
		"deletedItemId": deleteIidList,
		"updatedItemId": updateIidList,
		"building":      building,
		"user":          u,
	})
}

func ResetUserBuilding(c *fiber.Ctx) error {
	var body cooo.BaseRequestWithInt

	err := c.BodyParser(&body)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"success": false,
			"message": "Cannot parse JSON",
		})
	}
	models.GetInstance().ResetUserBuilding(body.Id)
	return c.Status(fiber.StatusOK).JSON(fiber.Map{
		"success": true,
	})
}

func AddCoin(c *fiber.Ctx) error {
	var body cooo.AddCoin

	err := c.BodyParser(&body)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"success": false,
			"message": "Cannot parse JSON",
		})
	}
	models.GetInstance().AddCoin(body.Coin, body.Uid)
	return c.Status(fiber.StatusOK).JSON(fiber.Map{
		"success": true,
	})
}

func UsersFriendly(c *fiber.Ctx) error {
	var body cooo.UsersFriendlyBetween

	err := c.BodyParser(&body)
	if err != nil {
		return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
			"success": false,
			"message": "Cannot parse JSON",
		})
	}
	u1 := models.GetInstance().FindUserFriendlyBetween(body.Uid, body.Friendid)
	return c.Status(fiber.StatusOK).JSON(fiber.Map{
		"success":      true,
		"relationShip": u1,
	})
}
